package com.hc.app.frequencycontrol;

import com.hc.app.annatation.SupplierThrowWithoutParam;
import com.hc.app.dto.FrequencyControlDto;
import com.hc.app.exception.FrequencyControlErrorEnum;
import com.hc.app.exception.FrequencyControlException;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public abstract class AbstractFrequencyControl<K extends FrequencyControlDto> {

    @PostConstruct
    protected void registerFrequencyToFactory(){
        FrequencyStrategyFactory.registFrequency(getStrategyName(),this);
    }

    @SuppressWarnings("unchecked") //表示泛型集合赋值擦除
    public <T> T executeWithFrequencyControlList(List<K> frequencyControlList,SupplierThrowWithoutParam<T> supplier) throws Throwable {
        boolean existsFrequencyControlHasNullKey = frequencyControlList.stream().anyMatch(frequency -> ObjectUtils.isEmpty(frequency.getKey()));
        if (existsFrequencyControlHasNullKey){
            throw new RuntimeException("限流策略的Key字段不允许出现空值");
        }
        Map<String, K> frequencyControlMap = frequencyControlList.stream().collect(Collectors.groupingBy(K::getKey, Collectors.collectingAndThen(Collectors.toList(), list -> list.get(0))));
        return executeWithFrequencyControlMap(frequencyControlMap,supplier);
    }

    /**
     * 执行限流策略
     */
    private <T> T executeWithFrequencyControlMap(Map<String, K> frequencyControlMap, SupplierThrowWithoutParam<T> supplier) throws Throwable {
        if (reachRateLimit(frequencyControlMap)) {
            throw new FrequencyControlException(FrequencyControlErrorEnum.FREQUENCY_LIMIT);
        }
        try {
            return supplier.get();
        } finally {
            //不管成功还是失败，都增加次数
            addFrequencyControlStatisticsCount(frequencyControlMap);
        }
    }


    /**
     * 判断是否达到限流阈值
     * @return
     */
    protected abstract boolean reachRateLimit(Map<String, K> frequencyControlMap);

    /**
     * 增加限流统计次数
     */
    protected abstract void addFrequencyControlStatisticsCount(Map<String, K> frequencyControlMap);

    /**
     * 获取限流策略名称
     * @return
     */
    protected abstract String getStrategyName();
}
